//==================================================================
//  Copyright (C) 2006-2007  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
//==================================================================

//#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <winuser.h>
#include <CommCtrl.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gl/glew.h>
#include <gl/wglew.h>
#include "psys.h"
#include "gfxutils.h"
#include "font.h"
#include "pwindow.h"
#include "gadget.h"

#pragma comment( lib, "Comctl32.lib" )

//==================================================================
///
//==================================================================
namespace WinSys
{

static FontUni	*_fontunip;

};

//==================================================================
enum {
	X1 = 0,
	Y1,
	X2,
	Y2
};

//==================================================================
static HINSTANCE	_hinstance;
static HFONT		_hfont;
static HGLRC		_main_hrc;
static TCHAR		_win_class_name[128];
static TCHAR		_win_bg_class_name[128];

//==================================================================
Window::Window()
{
	Reset();
}

//==================================================================
Window::Window( const TCHAR *namep, Window *parentp,
			   void *userobjectp, WindowEvent::Callback event_callback,
			    int x, int y, int w, int h,
				win_init_flags flags,  void *menu_id/*=0 */ )
{
	if PTRAP_TRUE( win_init_quick( this, namep, parentp, userobjectp, event_callback, x, y, w, h, flags, menu_id ) != POK )
		throw "Window creation failed";
}

//==================================================================
Window::Window( const TCHAR *namep, Window *parentp,
				void *userobjectp, WindowEvent::Callback event_callback,
				AnchorType anch_type_x1, int anch_offset_x1,
				AnchorType anch_type_y1, int anch_offset_y1,
				AnchorType anch_type_x2, int anch_offset_x2,
				AnchorType anch_type_y2, int anch_offset_y2,
				win_init_flags flags,
				void *menu_id/*=0*/ )
{
	if PTRAP_TRUE( win_init_quick( this, namep, parentp, userobjectp, event_callback,
					anch_type_x1, anch_offset_x1,
					anch_type_y1, anch_offset_y1,
					anch_type_x2, anch_offset_x2,
					anch_type_y2, anch_offset_y2,
					flags, menu_id ) != POK )
		throw "Window creation failed";
}

//==================================================================
void Window::Reset()
{
	_nextp = NULL;
	_prevp = NULL;
	_childp = NULL;
	_parentp = NULL;

	_hwnd = NULL;
	_hdc = NULL;
	_hrc = NULL;
	_mswin_eventfilter = NULL;
	_mswin_userdatap = NULL;

	_name[0] = 0;

	_userobjectp = NULL;
	_event_callback = NULL;

	for (int i=0; i < 4; ++i)
	{
		_anch_typ[i] = ANCH_TYPE_FIXED;
		_anch_off[i] = 0;
	}

	_x = 0;
	_y = 0;
	_w = 0;
	_h = 0;

	_flags = 0;;
	_intflags = 0;
}

//==================================================================
static void remove_from_tree( Window *T )
{
	// remove from the message queue !
	if ( T->_hwnd )
		SetWindowLongPtr( T->_hwnd, GWLP_USERDATA, NULL );

	Window	*childp = T->_childp;
	for (; childp; childp = childp->_nextp)
	{
		PASSERT ( childp->_parentp == T );
		childp->_parentp = T->_parentp;
	}

	if ( T->_parentp )
	{
		if ( T->_parentp->_childp == T )
		{
			if ( T->_nextp )
				T->_parentp->_childp = T->_nextp;
			else
				T->_parentp->_childp = T->_childp;
		}

		Window	*siblingp = T->_parentp->_childp;
		while ( siblingp )
		{
			if ( siblingp->_nextp == T )
			{
				siblingp->_nextp = T->_nextp;
				break;
			}

			siblingp = siblingp->_nextp;
		}
	}
}

//==================================================================
Window::~Window()
{
	if ( _hwnd )
		DestroyWindow( _hwnd );
	else
		remove_from_tree( this );
}

//==================================================================
void win_viewport_get( Window *T, int xywh[4] )
{
/*	if ( T->_parentp )
	{
		xywh[0] = T->_x;
		xywh[1] = T->_parentp->h - T->_h - T->_y;
	}
	else*/
	{
		xywh[0] = 0;
		xywh[1] = 0;
	}
	xywh[2] = T->_w;
	xywh[3] = T->_h;
}

//==================================================================
static void draw_window( Window *T )
{
int		x2, y2;
int		x, y, w, h;

	x = T->_x;
	y = T->_parentp->_h - T->_h - T->_y;
	w = T->_w;
	h = T->_h;
	x2 = w - 1;
	y2 = h - 1;

	begin_2D_wh( w, h );

		glDisable( GL_TEXTURE_2D );
		glEnable( GL_BLEND );
		glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

		glColor4f( 0, 0, 0.3f, 0.8f );
		glBegin( GL_QUADS );
			glVertex2f( x2 * 0, y2 * 0 );
			glVertex2f( x2 * 1, y2 * 0 );
			glVertex2f( x2 * 1, y2 * 1 );
			glVertex2f( x2 * 0, y2 * 1 );
		glEnd();

		//glColor3f( 0.5f, 0.5f, 0.5f );
//		if ( T->_intflags & WIN_INTFLG_FOCUS )
//			glColor4f( 1, 0, 0, 1.0f );
//		else
			glColor4f( .7f, .7f, .7f, 1.0f );

		glBegin( GL_LINE_STRIP );
			glVertex2f( x2 * 0, y2 * 0 );
			glVertex2f( x2 * 1, y2 * 0 );
			glVertex2f( x2 * 1, y2 * 1 );
			glVertex2f( x2 * 0, y2 * 1 );
			glVertex2f( x2 * 0, y2 * 0 );
		glEnd();

		glDisable( GL_BLEND );

	end_2D();

}

//==================================================================
static void win_context_begin_ext( Window *T, int do_draw )
{
int		x, y, w, h;

	if ( (T->_intflags & WIN_INTFLG_OPENGL) == 0 )
		return;

	wglMakeCurrent( T->_hdc, T->_hrc );

	x = 0;
	y = 0;
	w = T->_w;
	h = T->_h;

	glViewport( x, y, w, h );

	glPushAttrib( GL_ENABLE_BIT | GL_TEXTURE_BIT );
	glDisable( GL_DEPTH_TEST );
	glDisable( GL_LIGHTING );
	glDisable( GL_BLEND );
}

//==================================================================
static void setContext( Window *T )
{
	if ( (T->_intflags & WIN_INTFLG_OPENGL) == 0 )
		return;

	wglMakeCurrent( T->_hdc, T->_hrc );
}

//==================================================================
static void win_context_end( Window *T )
{
	if ( (T->_intflags & WIN_INTFLG_OPENGL) == 0 )
		return;

	glPopAttrib(); 
}

//==================================================================
PError win_init_quick( Window *T, const TCHAR *namep, Window *parentp,
					 void *userobjectp, WindowEvent::Callback event_callback,
					 int x, int y, int w, int h,
					 win_init_flags flags,
					 void *menu_id )
{
	return win_init_quick( T, namep, parentp,
				userobjectp, event_callback,
				Window::ANCH_TYPE_FIXED, x,
				Window::ANCH_TYPE_FIXED, y,
				Window::ANCH_TYPE_THIS_X1, w,
				Window::ANCH_TYPE_THIS_Y1, h,
				flags,
				menu_id );
}

//==================================================================
static void ggetDispUpdateCback( void *datap )
{
	((Window *)datap)->Invalidate();
}

//==================================================================
static void menuDispUpdateCback( void *datap )
{
	((Window *)datap)->Invalidate();
}

//==================================================================
PError win_init_quick( Window *T, const TCHAR *namep, Window *parentp,
			   void *userobjectp, WindowEvent::Callback event_callback,
			   Window::AnchorType anch_type_x1, int anch_offset_x1,
			   Window::AnchorType anch_type_y1, int anch_offset_y1,
			   Window::AnchorType anch_type_x2, int anch_offset_x2,
			   Window::AnchorType anch_type_y2, int anch_offset_y2,
			   win_init_flags flags,
			   void *menu_id )
{
	T->Reset();

	if ( T->_parentp = parentp )
	{
		if ( parentp->_childp )	// has children
		{
			T->_nextp = parentp->_childp;
			parentp->_childp->_prevp = T;
		}

		parentp->_childp = T;
	}
	else
	{
		// automatically a system window if has no parent !!!
		flags = (win_init_flags)(flags | (u_int)WIN_INIT_FLG_SYSTEM);
	}
	flags = (win_init_flags)(flags | (u_int)WIN_INIT_FLG_SYSTEM);

	_tcscpy_s( T->_name, namep );

	T->_userobjectp		= userobjectp;
	T->_event_callback	= event_callback;

	T->_anch_typ[0] = anch_type_x1;
	T->_anch_typ[1] = anch_type_y1;
	T->_anch_typ[2] = anch_type_x2;
	T->_anch_typ[3] = anch_type_y2;

	T->_anch_off[0] = anch_offset_x1;
	T->_anch_off[1] = anch_offset_y1;
	T->_anch_off[2] = anch_offset_x2;
	T->_anch_off[3] = anch_offset_y2;

//	if ( flags & WIN_INIT_FLG_INVISIBLE )
		T->_flags = 0;
//	else
//		T->_flags = WIN_FLG_SHOW;

	T->Rethink();

	// marks the begin of window event action
	T->_intflags |= (win_intflags)flags;
	T->_event.winp = T;

	T->_intflags |= (win_intflags)WIN_INTFLG_CREATIONCOMPLETED;

	if ( flags & WIN_INIT_FLG_SYSTEM )
	{
	RECT	out_rect, in_rect;
	const TCHAR	*classp;
	HWND		parent_hwnd;
	u_int		style;

		if ( T->_intflags & (WIN_INIT_FLG_OPENGL | WIN_INTFLG_DONT_CLEAR) )
			classp = _win_class_name;
		else
			classp = _win_bg_class_name;

		parent_hwnd = NULL;
		style = 0;

		if ( flags & WIN_INIT_FLG_HSCROLL )
			style |= WS_HSCROLL;

		if ( flags & WIN_INIT_FLG_VSCROLL )
			style |= WS_VSCROLL;

		if ( T->_parentp )
		{
			if ( T->_parentp->_hwnd )
				parent_hwnd = T->_parentp->_hwnd;

			if ( flags & WIN_INIT_FLG_POPUP )
				style = WS_POPUPWINDOW | WS_CAPTION;
			else
				style = WS_CHILD;
		}
		else
		{
			style = WS_OVERLAPPEDWINDOW;
		}
		style |= WS_CLIPCHILDREN | WS_CLIPSIBLINGS;

		u_int	ex_flags = WS_EX_ACCEPTFILES;
		if ( flags & WIN_INIT_FLG_CLIENTEDGE )
		{
			ex_flags |= WS_EX_CLIENTEDGE;
		}

		if ( flags & WIN_INIT_FLG_TOOLWINDOW )
		{
			ex_flags |= WS_EX_TOOLWINDOW;//WS_EX_PALETTEWINDOW;
		}

		T->_hwnd = CreateWindowEx( ex_flags, classp, T->_name, style,
									CW_USEDEFAULT, CW_USEDEFAULT, T->_w, T->_h, parent_hwnd,
									(HMENU)menu_id,
									_hinstance, (LPVOID)T );

		if NOT( T->_hwnd )
			return PERROR;

		GetWindowRect( T->_hwnd, &out_rect );
		GetClientRect( T->_hwnd, &in_rect );

		SetWindowPos( T->_hwnd, NULL, 0, 0,
					  T->_w + ((out_rect.right - out_rect.left) - (in_rect.right - in_rect.left)),
					  T->_h + ((out_rect.bottom - out_rect.top) - (in_rect.bottom - in_rect.top)),
					  SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS );

		if NOT( flags & WIN_INIT_FLG_INVISIBLE )
			ShowWindow( T->_hwnd, SW_SHOW );

		//UpdateWindow( T->_hwnd );
	}

	win_context_begin_ext( T, 0 );		

		T->GetGGETManager()._base_manager.SetDispUpdateCallback( ggetDispUpdateCback, T );
		T->GetGGETManager()._base_manager.SetEventFilterCallback( GGET_Manager::EventFilter_s, &T->GetGGETManager() );
		
		T->_main_menu._base_manager.SetDispUpdateCallback( menuDispUpdateCback, T );
		T->_main_menu._base_manager.SetEventFilterCallback( Menu::EventFilter_s, &T->_main_menu );

	win_context_end( T );

	T->SetFocus();

	return POK;
}

//==================================================================
void win_mswin_event_callback_set( Window *T, win_mswin_eventfilter_t mswin_eventfilter, void *userdatap )
{
	T->_mswin_eventfilter = mswin_eventfilter;
	T->_mswin_userdatap = userdatap;
}

//==================================================================
void win_invalidate( Window *T )
{
/*	while ( T && T->_hwnd == 0 )
	{
		T = T->_parentp;
	}

	if ( T && T->_hwnd )
*/
	{
		InvalidateRect( T->_hwnd, NULL, FALSE );
	}
}

//==================================================================
void Window::Invalidate()
{
	InvalidateRect( _hwnd, NULL, FALSE );
}

//==================================================================
bool Window::IsMousePointerInside() const
{
	POINT	pt;
	GetCursorPos( &pt );

	RECT	rc;
	GetWindowRect( _hwnd, &rc );

	return pt.x >= rc.left && pt.x < rc.right && pt.y >= rc.top && pt.y < rc.bottom;
}

//==================================================================
void Window::Show( int onoff, bool do_minimize/*=false */ )
{
	if ( onoff )
	{
		_intflags |= WIN_INTFLG_INVISIBLE;	
		if ( _hwnd )
			ShowWindow( _hwnd, do_minimize ? SW_MINIMIZE : SW_SHOW );

//		_flags |= WIN_FLG_SHOW;
	}
	else
	{
		_intflags &= ~WIN_INTFLG_INVISIBLE;
		if ( _hwnd )
			ShowWindow( _hwnd, do_minimize ? SW_MINIMIZE : SW_HIDE );

//		_flags &= ~WIN_FLG_SHOW;
	}


	Window	*topp = this;
	while ( topp->_parentp )
	{
		topp = topp->_parentp;
	}

	if ( topp )
		topp->Rethink();
}

//==================================================================
bool Window::IsShowing() const
{
	return IsWindowVisible( _hwnd ) != 0;
}

//==================================================================
bool Window::IsIconic() const
{
	return ::IsIconic( _hwnd ) != 0;
}

//==================================================================
int Window::get_anch_fixed_and_parent( AnchorType anch, int *outvalp, int val_idx )
{
	Window *parentp = _parentp;

	switch ( anch )
	{
	case ANCH_TYPE_PARENT_X1:	*outvalp = parentp->_x + _anch_off[val_idx];				return 1;
	case ANCH_TYPE_PARENT_Y1:	*outvalp = parentp->_y + _anch_off[val_idx];				return 1;
	case ANCH_TYPE_PARENT_X2:	*outvalp = parentp->_x + parentp->_w + _anch_off[val_idx];	return 1;
	case ANCH_TYPE_PARENT_Y2:	*outvalp = parentp->_y + parentp->_h + _anch_off[val_idx];	return 1;
	case ANCH_TYPE_FIXED:		*outvalp = _anch_off[val_idx];							return 1;

	default:
		return 0;
	}
}

//==================================================================
int Window::get_anch_local_relative( AnchorType anch, int vals[4], int *outvalp, int val_idx )
{
	int	is_showing = 1;//(_flags & WIN_FLG_SHOW) ? 1 : 0;
	switch ( anch )
	{
	case ANCH_TYPE_THIS_X1:	*outvalp = vals[X1] + is_showing * _anch_off[val_idx]; return 1;
	case ANCH_TYPE_THIS_Y1:	*outvalp = vals[Y1] + is_showing * _anch_off[val_idx]; return 1;
	case ANCH_TYPE_THIS_X2:	*outvalp = vals[X2] + is_showing * _anch_off[val_idx]; return 1;
	case ANCH_TYPE_THIS_Y2:	*outvalp = vals[Y2] + is_showing * _anch_off[val_idx]; return 1;

	default:
		return 0;
	}
}

//==================================================================
void Window::Rethink()
{
	int	isdone[4];
	int	vals[4];

	// get the values coming from the anchors requests
	for (int i=0; i < 4; ++i)
		isdone[i] = get_anch_fixed_and_parent( _anch_typ[i], &vals[i], i );

	for (int i=0; i < 4; ++i)
		if NOT( isdone[i] )
			isdone[i] = get_anch_local_relative( _anch_typ[i], vals, &vals[i], i );

	PASSERT( isdone[0] && isdone[1] && isdone[2] && isdone[3] );

	// refresh the window coords based on the new calculations
	_x = vals[X1];
	_y = vals[Y1];
	_w = vals[X2] - vals[X1];
	_h = vals[Y2] - vals[Y1];

	if ( _parentp )
	{
		SetWindowPos( _hwnd, NULL, _x, _y, _w, _h, SWP_NOZORDER | SWP_NOCOPYBITS );
	}

	// copy the window size information into the even structure.. just in case
	_event.win_w = _w;
	_event.win_h = _h;

	if ( _childp )
		_childp->Rethink();

	if ( _nextp )
		_nextp->Rethink();
}

//==================================================================
void Window::SetFocus()
{
	::SetFocus( _hwnd );
}

//==================================================================
void Window::SetTitle( const TCHAR *titlep )
{
	SetWindowText( _hwnd, titlep );
}

//==================================================================
void Window::PostResize()
{
	RECT	rc;
	::GetWindowRect( _hwnd, &rc );
	SetWindowPos( _hwnd, NULL, 0, 0, rc.right, rc.bottom,
		SWP_NOACTIVATE |
		SWP_NOCOPYBITS |
		SWP_NOMOVE |
		SWP_NOOWNERZORDER |
		SWP_NOZORDER );
}

//==================================================================
void win_destroy_real( Window *T )
{
	T->_hwnd = NULL;
	remove_from_tree( T );
}

//==================================================================
void win_destroy( Window *T )
{
	if ( T->_hwnd )
		DestroyWindow( T->_hwnd );
	else
		remove_from_tree( T );
}

//==================================================================
void win_flags_set( Window *T, win_flags flags, int onoff )
{
	if ( onoff )
		T->_flags |= flags;
	else
		T->_flags &= ~flags;
}

//==================================================================
int win_is_point_inside( Window *T, int px, int py )
{
	return ( px >= T->_x && py >= T->_y && px < (T->_x+T->_w) && py < (T->_y+T->_h) );
}

//==================================================================
static void rethink_childrens( Window *T )
{
Window	*childp;

	if ( childp = T->_childp )
	{
		do {
			childp->Rethink();
			childp = childp->_nextp;
		} while ( childp );
	}
}

//==================================================================
void win_move( Window *T, int x, int y )
{
	if NOT( T->_intflags & WIN_INTFLG_SYSTEM )
	{
		T->_x = x;
		T->_y = y;
		rethink_childrens( T );
	}
	else
	{
		SetWindowPos( T->_hwnd, NULL, x, y, 0, 0, SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOSIZE );
	}
}

//==================================================================
void win_globalpos_get( Window *T, int *xp, int *yp, int *wp, int *hp )
{
Window	*winp;

	winp = T;
	while ( winp->_parentp )
		winp = winp->_parentp;

	{
	RECT	rc;

		GetWindowRect( winp->_hwnd, &rc );

		if ( xp )	*xp = rc.left + T->_x;
		if ( yp )	*yp = rc.top + T->_y;
		if ( wp )	*wp = winp->_w;
		if ( hp )	*hp = winp->_h;
	}
}

//==================================================================
void win_resize( Window *T, int w, int h )
{
	T->_w = w;
	T->_h = h;
	rethink_childrens( T );

	if ( T->_intflags & WIN_INTFLG_SYSTEM )
	{
	RECT	rc;

		SetWindowPos( T->_hwnd, NULL, 0, 0, w, h,
						SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOMOVE );
		GetClientRect( T->_hwnd, &rc );
		SetWindowPos( T->_hwnd, NULL, 0, 0, w + (w - rc.right), h + (h - rc.bottom),
						SWP_NOZORDER | SWP_NOCOPYBITS | SWP_NOMOVE );
	}
}

//==================================================================
static Window *get_topmost( Window *T )
{
	// find the topmost parent parent
	Window	*topmostp = T;
	while ( topmostp->_parentp )
		topmostp = topmostp->_parentp;

	return topmostp;
}
/*
//==================================================================
static __inline void rec_dir( treetrav_t *travp, Window *winp, TCHAR dir )
{
//	PDEBUG_PRINTF( "%s.", winp->name );
	treetrav_rec_dir( travp, dir );
}
//==================================================================
static __inline void rec_val( treetrav_t *travp, Window *winp )
{
//	PDEBUG_PRINTF( "%s.", winp->name );
	treetrav_rec_val( travp, 1 );
}
*/

//==================================================================
void Window::destroyWithChildren( WindowEvent *eventp )
{
	Window	*topmostp = get_topmost( this );

	if ( _childp )
		_childp->destroyWithChildren( eventp );

	remove_from_tree( this );

	if ( _event_callback )
	{
		setContext( this );
		_event_callback( _userobjectp, eventp );
	}
}

//==================================================================
static void base_eventfilter( Window *T, HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	switch ( message )
	{
	// these three messages are only available for the window that has focus !
	case WM_CHAR:
	case WM_KEYDOWN:
	case WM_KEYUP:
		break;

	case WM_RBUTTONDOWN:
		break;

	case WM_LBUTTONDOWN:
		T->_intflags |= WIN_FLG_MOUSE_CURSOR_IN;
		T->SetFocus();
		if ( !win_global_capture_set( T ) )
		{
			SetCapture( hWnd );
		}
		break;

	case WM_MOUSEMOVE:
		break;

	case WM_LBUTTONUP:
		if ( win_global_capture_get() == T )
		{
			win_global_capture_release( T );
			ReleaseCapture();
		}
		break;
	}
}

//==================================================================
int Window::eventFilter( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{
	WindowEvent		event;

	event.ms_hwnd		= hWnd;
	event.ms_message	= message;
	event.ms_wparam		= wParam;
	event.ms_lparam		= lParam;

	switch ( message )
	{
	case WM_CREATE:
		event.type = WindowEvent::ETYPE_CREATE;
		break;

	case WM_ACTIVATE:
		if ( LOWORD(wParam) == 0 )
			event.type = WindowEvent::ETYPE_DEACTIVATE;
		else
			event.type = WindowEvent::ETYPE_ACTIVATE;
		break;

	case WM_LBUTTONDOWN:
	case WM_RBUTTONDOWN:
		if ( message == WM_LBUTTONDOWN )
		{
			event.type = WindowEvent::ETYPE_LBUTTONDOWN;
			event.mouse_lbutton = 1;
		}
		else
		{
			event.type = WindowEvent::ETYPE_RBUTTONDOWN;
			event.mouse_rbutton = 1;
		}

		event.mouse_x = (short)LOWORD(lParam);
		event.mouse_y = (short)HIWORD(lParam);

		if ( wParam & MK_CONTROL )
			event.keymod_ctrl = 1;
		else
			if ( wParam & MK_SHIFT )
				event.keymod_shift = 1;
		break;

	case WM_LBUTTONUP:
	case WM_RBUTTONUP:
		if ( message == WM_LBUTTONUP )
			event.type = WindowEvent::ETYPE_LBUTTONUP;
		else
			event.type = WindowEvent::ETYPE_RBUTTONUP;

		event.mouse_x = (short)LOWORD(lParam); 
		event.mouse_y = (short)HIWORD(lParam); 
		break;

	case WM_MOUSEMOVE:
		if NOT( _intflags & WIN_INTFLG_MOUSE_IN_CLIENT )
		{
			_intflags |= WIN_INTFLG_MOUSE_IN_CLIENT;

			TRACKMOUSEEVENT tme = { sizeof(tme) };
			tme.dwFlags = TME_LEAVE;
			tme.hwndTrack = hWnd;
			_TrackMouseEvent(&tme);
		}

		event.type = WindowEvent::ETYPE_MOUSEMOVE;
		event.mouse_x = (short)LOWORD(lParam); 
		event.mouse_y = (short)HIWORD(lParam); 
		event.mouse_lbutton = (GetAsyncKeyState( VK_LBUTTON ) & 0x8000) ? 1 : 0;
		event.mouse_rbutton = (GetAsyncKeyState( VK_RBUTTON ) & 0x8000) ? 1 : 0;
		break;

	case WM_MOUSELEAVE:
		_intflags &= ~WIN_INTFLG_MOUSE_IN_CLIENT;

		event.type = WindowEvent::ETYPE_MOUSEMOVEDOUT;
		event.mouse_x = (short)LOWORD(lParam); 
		event.mouse_y = (short)HIWORD(lParam); 
		event.mouse_lbutton = (GetAsyncKeyState( VK_LBUTTON ) & 0x8000) ? 1 : 0;
		event.mouse_rbutton = (GetAsyncKeyState( VK_RBUTTON ) & 0x8000) ? 1 : 0;
		break;

	case WM_SIZE:
		{
			event.type = WindowEvent::ETYPE_WINRESIZE;

			if ( _parentp == NULL )
			{
				RECT	rc;
		
				GetClientRect( hWnd, &rc );
				SetAnchorOffset_X2( rc.right );		// assume it's the parent window
				SetAnchorOffset_Y2( rc.bottom );	//
			}
			Rethink();
		}
		break;

	case WM_COMMAND:
		event.type = WindowEvent::ETYPE_COMMAND;
		event.command = LOWORD(wParam);
		break;

	case WM_CHAR:
		event.type = WindowEvent::ETYPE_KEYCHAR;
		event.keychar = wParam;
		break;

	case WM_KEYDOWN:
		event.type = WindowEvent::ETYPE_KEYDOWN;
		event.keycode = wParam;
		break;

	case WM_KEYUP:
		event.type = WindowEvent::ETYPE_KEYUP;
		event.keycode = wParam;
		break;

	case WM_SHOWWINDOW:
		event.type = WindowEvent::ETYPE_SHOW;
		event.is_showing = (wParam != 0);
		break;

	case WM_PAINT:
		// DAVIDE - ummmmm - ValidateRect( hWnd, NULL );
		event.type = WindowEvent::ETYPE_PAINT;
		break;

	case WM_CLOSE:
		DestroyWindow( hWnd );
		return 0;

	case WM_DESTROY:
		event.type = WindowEvent::ETYPE_DESTROY;
		destroyWithChildren( &event );
		return 0;

	case WM_DROPFILES:
		event.type = WindowEvent::ETYPE_DROPFILES;
		ef_onDropFiles( hWnd, message, wParam, lParam, event );
		break;

	default:
		event.type = WindowEvent::ETYPE_UNKNOWN;
		return 0;
	}

	_event = event;
	_event.winp = this;
	_event.win_w = _w;
	_event.win_h = _h;


	// base filter tells whether this message can be handled by this window or not
	// also tells if this message must stop with this window (private focus handling)
	base_eventfilter( this, (HWND)event.ms_hwnd, event.ms_message, event.ms_wparam, event.ms_lparam );

	win_context_begin_ext( this, 0 );

	if ( event.type == WindowEvent::ETYPE_PAINT )
	{
		if ( _event_callback )
		{
			//if ( event.type == WindowEvent::ETYPE_PAINT && _hwnd == NULL )
			//	draw_window( this );

			if ( _event_callback( _userobjectp, &_event ) < 0 )
			{
				win_context_end( this );
				return -1;
			}
		}

		if ( GetGGETManager()._base_manager.EventFilter( &_event ) )
		{
			win_context_end( this );
			return 1;
		}

		if ( _main_menu._base_manager.EventFilter( &_event ) )
		{
			win_context_end( this );
			return 1;
		}
	}
	else
	{
		if ( _main_menu._base_manager.EventFilter( &_event ) )
		{
			win_context_end( this );
			return 1;
		}

		if ( GetGGETManager()._base_manager.EventFilter( &_event ) )
		{
			win_context_end( this );
			return 1;
		}

		if ( _event_callback )
		{
			//if ( event.type == WindowEvent::ETYPE_PAINT && _hwnd == NULL )
			//	draw_window( this );

			if ( _event_callback( _userobjectp, &_event ) < 0 )
			{
				win_context_end( this );
				return -1;
			}
		}
	}

	win_context_end( this );

	return 0;
}

//=============================================================================
void Window::ef_onDropFiles( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam, WindowEvent &out_event )
{
	HDROP		hDrop = (HDROP)wParam;
	TCHAR		filepath[8192];

	int	 n = DragQueryFile( hDrop, 0xffffffff, NULL, 0 );
	if ( n > 0 )
	{
		for (int i=0; i < n; ++i)
		{
			if ( DragQueryFile( hDrop, i, filepath, _countof(filepath)-1 ) > 0 )
			{
				out_event.dropfiles.push_back( new PSYS::tstring( filepath ) );
			}
		}
	}

	DragFinish( hDrop );
}

//==================================================================
static void SetDCPixelFormat( HDC hDC )
{
	int								nPixelFormat;
	static PIXELFORMATDESCRIPTOR	pfd = {
		sizeof(PIXELFORMATDESCRIPTOR),	// Size of this structure
		1,								// Version of this structure
		PFD_DRAW_TO_WINDOW |			// Draw to Window (not to bitmap)
		PFD_SUPPORT_OPENGL |			// Support OpenGL calls in window
		PFD_DOUBLEBUFFER |				// Double buffered mode
		PFD_STEREO_DONTCARE |
		PFD_DEPTH_DONTCARE |
		0,
		PFD_TYPE_RGBA,					// RGBA Color mode
		32,								// Want the display bit depth
		0,0,0,0,0,0,					// Not used to select mode
		0,0,							// Not used to select mode
		0,0,0,0,0,						// Not used to select mode
		24,								// Size of depth buffer
		8,								// 8 bit stencil
		0,								// Not used to select mode
		PFD_MAIN_PLANE,					// Draw in main plane
		0,								// Not used to select mode
		0,0,0
	};

	nPixelFormat = ChoosePixelFormat(hDC, &pfd);
	SetPixelFormat(hDC, nPixelFormat, &pfd);
}

//==================================================================
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT	ps;
HDC			hdc;
Window		*winp;

	if ( message == WM_CREATE )
	{
		CREATESTRUCT	*csp;

		csp = (CREATESTRUCT *)lParam;
		winp = (Window *)csp->lpCreateParams;
		SetWindowLongPtr( hWnd, GWLP_USERDATA, (LONG_PTR)winp );

		winp->_hdc = GetDC( hWnd );
		winp->_hwnd = hWnd;

		if ( winp->_intflags & WIN_INTFLG_OPENGL )
		{
			SetDCPixelFormat( winp->_hdc );
			winp->_hrc = wglCreateContext( winp->_hdc );

			if ( _main_hrc )
				wglShareLists( _main_hrc, winp->_hrc );
			else
				_main_hrc = winp->_hrc;

			wglMakeCurrent( winp->_hdc, winp->_hrc );

			// geez !
			glewInit();
			if ( wglSwapIntervalEXT )
				wglSwapIntervalEXT( 0 );

			// create the font once, for the first opengl window available
			static bool	font_created;
			if NOT( font_created )
			{
				font_created = true;
				FONT_Create();
			}
			if NOT( WinSys::_fontunip )
			{
				WinSys::_fontunip = new FontUni( 10 );
			}
		}
	}
//	else
	{
		// not necessarily available.. some messages happen before the create !!
		winp = (Window *)GetWindowLongPtr( hWnd, GWLP_USERDATA );
		if ( winp == NULL || (winp->_intflags & WIN_INTFLG_CREATIONCOMPLETED) == 0 )
			return DefWindowProc(hWnd, message, wParam, lParam);

		if ( message == WM_PAINT )
		{
			hdc = BeginPaint(hWnd, &ps);

			if ( winp->_intflags & WIN_INTFLG_OPENGL )
				wglMakeCurrent( winp->_hdc, winp->_hrc );
		}

		if ( !winp->_mswin_eventfilter ||
			 !winp->_mswin_eventfilter( winp->_userobjectp, winp, hWnd, message, wParam, lParam ) )
		{
			if ( winp->eventFilter( hWnd, message, wParam, lParam ) < 0 )
				return -1;
		}

		if ( message == WM_PAINT )
		{
			if ( winp->_intflags & WIN_INTFLG_OPENGL )
			{
				SwapBuffers( winp->_hdc );
			}

			EndPaint(hWnd, &ps);
		}
	}
/*
	switch (message) 
	{
	case WM_DESTROY:
		//PostQuitMessage(0);
		break;

	default:
		return 
	}
*/
	return DefWindowProc(hWnd, message, wParam, lParam);
}

//==================================================================
static ATOM register_class( HINSTANCE hInstance, HICON hicon, const TCHAR *szWindowClass, int usebg )
{
WNDCLASSEX wcex;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= hicon;//LoadIcon( hInstance, IDI_APPLICATION );
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= usebg ? (HBRUSH)(COLOR_WINDOW) : NULL;
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= hicon;//LoadIcon(wcex.hInstance, IDI_APPLICATION );//(LPCTSTR)IDI_SMALL);

	return RegisterClassEx(&wcex);
}

//==================================================================
///
//==================================================================
namespace WinSys
{

//==================================================================
void Init( HINSTANCE hinstance, HICON hicon, const TCHAR *win_classprefixp )
{
	if ( _hinstance )
	{
		PASSERT( 0 );
		return;
	}

	_hinstance = hinstance;

	_tcscpy_s( _win_class_name, win_classprefixp );
	_tcscat_s( _win_class_name, _T("_CLASS") );

	_tcscpy_s( _win_bg_class_name, win_classprefixp );
	_tcscat_s( _win_bg_class_name, _T("_BG_CLASS") );

	register_class( _hinstance, hicon, _win_class_name, 0 );
	register_class( _hinstance, hicon, _win_bg_class_name, 1 );
}

//==================================================================
HINSTANCE GetInstance()
{
	return _hinstance;
}

//==================================================================
void GetDeskSize( u_int &out_wd, u_int &out_he )
{
	HWND hwnd = GetDesktopWindow();

	RECT	rc;
	GetClientRect( hwnd, &rc );

	out_wd = rc.right;
	out_he = rc.bottom;
}

//==================================================================
FontUni	&GetFont()
{
	return *_fontunip;
}

};

//==================================================================
//==================================================================
//==================================================================
//==================================================================
static Window	*_capture_winp;

//==================================================================
int	win_global_capture_set( Window *T )
{
	if ( _capture_winp )
		return -1;

	_capture_winp = T;
	return 0;
}

//==================================================================
void win_global_capture_release( Window *T )
{
	if ( _capture_winp == T )
		_capture_winp = NULL;
}

//==================================================================
Window *win_global_capture_get(void)
{
	return _capture_winp;
}

//==================================================================
static int _vcursor_pos[2] = { -50, -50 };

void win_vcursor_pos_getv( int pos[2] )
{
	pos[0] = _vcursor_pos[0];
	pos[1] = _vcursor_pos[1];
}

//==================================================================
void win_vcursor_pos_set( int x, int y )
{
	_vcursor_pos[0] = x;
	_vcursor_pos[1] = y;
}

//==================================================================
static Window	*_context_stack[64];
static int		_context_stack_cnt;

//==================================================================
void win_push_context( Window *T )
{
	if ERR_FALSE( _context_stack_cnt < 64 )
		return;

	HGLRC	old = wglGetCurrentContext();

	if ( old != T->_hrc )
		wglMakeCurrent( T->_hdc, T->_hrc );
	
	_context_stack[ _context_stack_cnt++ ] = T;
}

//==================================================================
void win_pop_context()
{
	if ERR_FALSE( _context_stack_cnt > 0 )
		return;

	Window	*contextp = _context_stack[ --_context_stack_cnt ];

	HGLRC	old = wglGetCurrentContext();

	if ( old != contextp->_hrc )
		wglMakeCurrent( contextp->_hdc, contextp->_hrc );
}

//==================================================================
WINGDIAPI HGLRC WINAPI wglGetCurrentContext(VOID);
WINGDIAPI HDC   WINAPI wglGetCurrentDC(VOID);
WINGDIAPI PROC  WINAPI wglGetProcAddress(LPCSTR);
WINGDIAPI BOOL  WINAPI wglMakeCurrent(HDC, HGLRC);
